#include "stdafx.h"
#include "Camera.h"
//Camera* Camera::m_instance = NULL;
#define _USE_MATH_DEFINES
#include <math.h>

const float X_ANGLE_LIMIT = 75.0f;
const float X_LIMIT = (float)sin((90.0f - X_ANGLE_LIMIT) * M_PI / 180);

const char REFRESH_FLAG_WORLD = 0x01;
const char REFRESH_FLAG_VIEW = 0x02;
const char REFRESH_FLAG_BOTH = REFRESH_FLAG_WORLD | REFRESH_FLAG_VIEW;

Camera::Camera()
{
	// You can read Camera's parameters from SceneManager.txt
	m_position = Vector3(0.0f, 0.5f, 2.0f);
	m_target = Vector3( 0.0f, 0.5f, 0.0f );
	m_up = Vector3( 0.0f, 1.0f, 0.0f );

	m_refreshFlags = REFRESH_FLAG_BOTH;
}
void Camera::Init()
{
	m_view.SetIdentity();
	mProjectionMatrix.SetIdentity();
	mProjectionMatrix.SetPerspective(PERSPECTIVE_ANGLE, WINDOW_WIDTH/WINDOW_HEIGHT, PERSPECTIVE_NEAR, PERSPECTIVE_FAR);

}
void Camera::Update()
{
	CalculateViewMatrix();
	
}
Matrix Camera::getCamera()
{
	return m_view*mProjectionMatrix; //nha voi worldmatrix = MVPmatrix;????
}

//Camera* Camera::GetInstance()
//{
//	if(m_instance == NULL)
//	{
//		m_instance = new Camera;
//	}
//	return m_instance;
//}

//////////////////////////////////////////////////////////////////////////
// Forward movement of the camera takes place on the axis which connects
//   the camera position with the camera target. This axis is also known as
//   the local Z axis
// Both position and target must be updated with the same amount
void Camera::MoveW(float deltaTime)
{
	// Given Vector3 A and B, A - B represents the direction from B towards A
	// Forward movement has the direction from camera position toward the target
	Vector3 deltaMove = (m_target - m_position).Normalize() * deltaTime;
	
	m_position += deltaMove;
	m_target += deltaMove;

	m_refreshFlags = REFRESH_FLAG_BOTH;
}

//////////////////////////////////////////////////////////////////////////
// Moving camera backward takes place on the axis between the camera position 
//   and the target, but in a direction away from the target (opposite to 
//   moving forward).
void Camera::MoveS(float deltaTime)
{
	MoveW( -deltaTime );
}

//////////////////////////////////////////////////////////////////////////
// Strife movement takes place on X axis in local space. The local X axis
//   of the camera is given by the cross product between the local Z axis (the
//   axis between the camera position and the target) the the up vector
// When computing strife movement, both the target and the position must be
//   updated with the same value, in order to keep the camera orientation unchanged.
void Camera::MoveD(float deltaTime)
{
	Vector3 zaxis = (m_position - m_target).Normalize();
	Vector3 deltaMove = (m_up.Cross(zaxis)).Normalize() * deltaTime;

	m_position += deltaMove;
	m_target += deltaMove;

	m_refreshFlags = REFRESH_FLAG_BOTH;
}

//////////////////////////////////////////////////////////////////////////
// The left strife movement is takes place on X axis in the negative direction
//   (i.e. opposed to the right strife)
void Camera::MoveA(float deltaTime)
{
	MoveD( -deltaTime );
}

//////////////////////////////////////////////////////////////////////////
// The up/down movement usually takes place on the UP direction which is 
//   required in a look-at algorithm.
void Camera::MoveUp(float deltaTime)
{
	Vector3 deltaMove = m_up * deltaTime;

	m_position += deltaMove;
	m_target += deltaMove;

	m_refreshFlags = REFRESH_FLAG_BOTH;
}

//////////////////////////////////////////////////////////////////////////
// The DOWN movement is opposed to the UP movement.
void Camera::MoveDown(float deltaTime)
{
	MoveUp( -deltaTime );
}

//////////////////////////////////////////////////////////////////////////
// In a look-at camera, rotations are easier to apply in the local space.
// In local space, the target position is always (0.0, 0.0, -distance(position, target) )
// For Up/Down rotations we will rotate this target in local space a around the X axis
//   (the camera is always at 0,0,0 in local space) and then apply the world matrix
//   to transform the new target in world space
// We need to limit the rotation Up and Down to a certain angle (75 degrees is the current limit)
void Camera::RotUp(float deltaTime)
{
	float rotSpeed = 0.3f;
	Vector4 localTgt( 0.0f, 0.0f, -(m_position - m_target).Length(), 1.0f );
	Vector4 tmp = Matrix().SetRotationX( deltaTime * rotSpeed ) * localTgt;
	Vector4 newTgt4 = CalculateWorldMatrix() * tmp;
	Vector3 newTgt( newTgt4.x, newTgt4.y, newTgt4.z );

	Vector3 xaxis, zaxis;
	zaxis = (m_position - newTgt).Normalize();
	xaxis = m_up.Cross(zaxis);

	if (xaxis.Length() >= X_LIMIT)
	{
		m_target = newTgt;
		m_refreshFlags = REFRESH_FLAG_BOTH;
	}
}

//////////////////////////////////////////////////////////////////////////
// Rotating camera down means rotating around the X axis in a counter-clockwise direction. 
void Camera::RotDown(float deltaTime)
{
	RotUp( -deltaTime );
}

//////////////////////////////////////////////////////////////////////////
// Left and right rotations mean rotating around the Y axis. There are no 
//   restrictions here
void Camera::RotLeft(float deltaTime)
{
	float rotSpeed = 0.3f;
	Vector4 localTgt( 0.0f, 0.0f, -(m_position - m_target).Length(), 1.0f );
	Vector4 tmp = Matrix().SetRotationY( deltaTime * rotSpeed ) * localTgt;
	Vector4 newTgt4 = CalculateWorldMatrix() * tmp;
	m_target = Vector3( newTgt4.x, newTgt4.y, newTgt4.z );

	m_refreshFlags = REFRESH_FLAG_BOTH;
}

void Camera::RotRight(float deltaTime)
{
	RotLeft( -deltaTime );

	m_refreshFlags = REFRESH_FLAG_BOTH;

}
void Camera::destroy()
{
	/*if(m_instance)
		delete m_instance;
	m_instance = NULL;*/
}


//////////////////////////////////////////////////////////////////////////
// We do not use rotation around the Z axis in order to avoid tilting the camera
//   or even turning it upside down.

Matrix& Camera::CalculateWorldMatrix()
{
	if ( (m_refreshFlags & REFRESH_FLAG_WORLD) == 0 )
	{
		return m_world;
	}

	m_refreshFlags &= ~REFRESH_FLAG_WORLD;

	Vector3 xaxis, yaxis, zaxis;
	zaxis = (m_position - m_target).Normalize();
	xaxis = (m_up.Cross(zaxis)).Normalize();
	yaxis = (zaxis.Cross(xaxis)).Normalize();
	m_world.m[0][0] = xaxis.x; m_world.m[0][1] = xaxis.y; m_world.m[0][2] = xaxis.z; m_world.m[0][3] = 0.0f;
	m_world.m[1][0] = yaxis.x; m_world.m[1][1] = yaxis.y; m_world.m[1][2] = yaxis.z; m_world.m[1][3] = 0.0f;
	m_world.m[2][0] = zaxis.x; m_world.m[2][1] = zaxis.y; m_world.m[2][2] = zaxis.z; m_world.m[2][3] = 0.0f;
	m_world.m[3][0] = m_position.x;
	m_world.m[3][1] = m_position.y;
	m_world.m[3][2] = m_position.z;
	m_world.m[3][3] = 1.0f;
	return m_world;
}

Matrix& Camera::CalculateViewMatrix()
{
	if ( (m_refreshFlags & REFRESH_FLAG_VIEW) == 0 )
	{
		return m_view;
	}

	m_refreshFlags &= ~REFRESH_FLAG_VIEW;

	Vector3 xaxis, yaxis, zaxis;
	zaxis = (m_position - m_target).Normalize();
	xaxis = (m_up.Cross(zaxis)).Normalize();
	yaxis = (zaxis.Cross(xaxis)).Normalize();
	m_view.m[0][0] = xaxis.x; m_view.m[0][1] = yaxis.x; m_view.m[0][2] = zaxis.x; m_view.m[0][3] = 0.0f;
	m_view.m[1][0] = xaxis.y; m_view.m[1][1] = yaxis.y; m_view.m[1][2] = zaxis.y; m_view.m[1][3] = 0.0f;
	m_view.m[2][0] = xaxis.z; m_view.m[2][1] = yaxis.z; m_view.m[2][2] = zaxis.z; m_view.m[2][3] = 0.0f;
	m_view.m[3][0] = -m_position.Dot( xaxis );
	m_view.m[3][1] = -m_position.Dot( yaxis );
	m_view.m[3][2] = -m_position.Dot( zaxis );
	m_view.m[3][3] = 1.0f;

	return m_view;
}
